package com.jungle.system.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.jungle.common.core.domain.OptionVo;
import com.jungle.common.core.domain.PageQuery;
import com.jungle.common.core.page.TableDataInfo;
import com.jungle.common.utils.BeanCopyUtils;
import com.jungle.common.utils.GeometryUtils;
import com.jungle.common.utils.StringUtils;
import com.jungle.framework.utils.AutoComputeUtils;
import com.jungle.system.domain.CommonArea;
import com.jungle.system.domain.bo.CommonAreaBo;
import com.jungle.system.domain.vo.CommonAreaGeoVo;
import com.jungle.system.domain.vo.CommonAreaSelectVo;
import com.jungle.system.domain.vo.CommonAreaVo;
import com.jungle.system.repository.CommonAreaRepository;
import com.jungle.system.service.ICommonAreaService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 地域Service业务层处理
 *
 * @author caomin
 * @date 2022-11-13
 */
@RequiredArgsConstructor
@Service
public class CommonAreaServiceImpl implements ICommonAreaService {

    private final CommonAreaRepository repository;

    private final EntityManager entityManager;


    @Override
    public CommonArea queryByCode(String code) {
        return repository.findOneByCode(code);
    }

    @Override
    public List<CommonAreaSelectVo> queryByParent(String code) {
        return repository.findAllByParentCode(code);
    }


    /**
     * 查询地域
     */
    @Override
    public CommonAreaVo queryById(String id) {
        return repository.findById(id).map(v -> BeanCopyUtils.copy(v, CommonAreaVo.class)).orElse(null);
    }


    private Specification<CommonArea> getSpecification(CommonAreaBo bo) {
        return (root, query, cb) -> {
            Map<String, Object> params = bo.getParams();
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.isNotBlank(bo.getAdCode())) {
                predicates.add(cb.equal(root.get("adCode"), bo.getAdCode()));
            }
            if (StringUtils.isNotBlank(bo.getCenter())) {
                predicates.add(cb.equal(root.get("center"), bo.getCenter()));
            }
            if (StringUtils.isNotBlank(bo.getCityCode())) {
                predicates.add(cb.equal(root.get("cityCode"), bo.getCityCode()));
            }
            if (StringUtils.isNotBlank(bo.getCountyCode())) {
                predicates.add(cb.equal(root.get("countyCode"), bo.getCountyCode()));
            }
            if (StringUtils.isNotBlank(bo.getFullName())) {
                predicates.add(cb.like(root.get("fullName"), "%" + bo.getFullName() + "%"));
            }
            if (StringUtils.isNotBlank(bo.getLevel())) {
                predicates.add(cb.equal(root.get("level"), bo.getLevel()));
            }
            if (StringUtils.isNotBlank(bo.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + bo.getName() + "%"));
            }
            if (StringUtils.isNotBlank(bo.getParentCode())) {
                predicates.add(cb.equal(root.get("parentCode"), bo.getParentCode()));
            }
            if (StringUtils.isNotBlank(bo.getPolyline())) {
                predicates.add(cb.equal(root.get("polyline"), bo.getPolyline()));
            }
            if (StringUtils.isNotBlank(bo.getProvinceCode())) {
                predicates.add(cb.equal(root.get("provinceCode"), bo.getProvinceCode()));
            }
            if (StringUtils.isNotBlank(bo.getTownCode())) {
                predicates.add(cb.equal(root.get("townCode"), bo.getTownCode()));
            }
            if (StringUtils.isNotBlank(bo.getCode())) {
                predicates.add(cb.equal(root.get("code"), bo.getCode()));
            }
            if (StringUtils.isNotBlank(bo.getChange())) {
                predicates.add(cb.equal(root.get("change"), bo.getChange()));
            }
            if (bo.getIndex() != null) {
                predicates.add(cb.equal(root.get("index"), bo.getIndex()));
            }
            if (StringUtils.isNotBlank(bo.getRemarks())) {
                predicates.add(cb.equal(root.get("remarks"), bo.getRemarks()));
            }
            if (StringUtils.isNotBlank(bo.getShortCode())) {
                predicates.add(cb.equal(root.get("shortCode"), bo.getShortCode()));
            }
            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
        };
    }


    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(CommonArea entity) {
        //TODO 做一些数据校验,如唯一约束
    }


    /**
     * 获取地域选项
     */
    @Override
    public List<OptionVo> optionSelect(String queryStr, String[] ids, String field, PageQuery pageQuery) {
        if (StringUtils.isNotBlank(field)) {
            return AutoComputeUtils.autoCompute(CommonArea.class, queryStr, field, pageQuery);
        }
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery(String[].class);
        Root root = cq.from(CommonArea.class);
        List<Predicate> predicates = new ArrayList<>();
        if (StringUtils.isNotBlank(queryStr)) {
            predicates.add(cb.like(root.get("fullName"), "%" + queryStr + "%"));
        }
        if (ArrayUtil.isNotEmpty(ids)) {
            predicates.add(root.get("adCode").in(ids));
        }
        PageRequest pageable = pageQuery.pageable();
        List<Object[]> result = entityManager.createQuery(cq
                .multiselect(root.get("adCode"), root.get("fullName"))
                .where(predicates.toArray(new Predicate[predicates.size()]))
            )
            .setFirstResult(pageable.getPageNumber() * pageable.getPageSize())
            .setMaxResults(Math.min(100, pageable.getPageSize()))
            .getResultList();

        return result.stream()
            .map(v -> new OptionVo(v[0].toString(), v[1].toString()))
            .collect(Collectors.toList());
    }

    /**
     * 查询地域列表
     */
    @Override
    public TableDataInfo<CommonArea> queryPageList(CommonAreaBo bo, PageQuery pageQuery) {
        Page<CommonArea> result = repository.findAll(Example.of(BeanCopyUtils.copy(bo, CommonArea.class)),
            PageRequest.of(pageQuery.getPageNum(), pageQuery.getPageSize()));
        return TableDataInfo.build(result);
    }


    /**
     * 新增地域
     */
    @Override
    public void insertByBo(CommonAreaBo bo) {
        CommonArea add = BeanCopyUtils.copy(bo, CommonArea.class);
        validEntityBeforeSave(add);
        repository.saveAndFlush(add);
        bo.setId(add.getId());
    }

    /**
     * 修改地域
     */
    @Override
    public void updateByBo(CommonAreaBo bo) {
        CommonArea update = BeanCopyUtils.copy(bo, CommonArea.class);
        validEntityBeforeSave(update);
        repository.save(update);
    }


    /**
     * 批量删除地域
     */
    @Override
    public void deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        repository.deleteAllById(ids);
    }

    @Override
    public List<String> findLocal(String areaCode) {
        ArrayList<String> local = new ArrayList<>();
        local.add(areaCode);
        do {
            final CommonArea commonArea = this.queryByCode(areaCode);
            if (commonArea != null && commonArea.getParentCode() != null && !commonArea.getParentCode().equals("100000")) {
                areaCode = commonArea.getParentCode();
                local.add(areaCode);
            } else {
                break;
            }
        } while (true);
        Collections.reverse(local);
        return local;
    }

    @Override
    public List<CommonArea> queryByCodes(Set<String> codes) {
        return repository.findAllByCodeIn(codes);
    }

    @Override
    public CommonAreaGeoVo queryGeoByAreaCode(String areaCode) {
        CommonArea oneByCode = repository.findOneByCode(areaCode);
        CommonAreaGeoVo copy = BeanCopyUtils.copy(oneByCode, CommonAreaGeoVo.class);
        if (null != oneByCode.getPolyline()) {
            copy.setPolyline(GeometryUtils.getWkt(oneByCode.getPolyline()));
        }
        return copy;
    }

    @Override
    public List<CommonAreaGeoVo> findGeoByParent(String areaCode) {
        List<CommonAreaGeoVo> geoVos = new ArrayList<>();
        List<CommonArea> allByParentCode = repository.findByParentCode(areaCode);
        if (!CollectionUtils.isEmpty(allByParentCode)) {
            for (CommonArea commonAreaSelectVo : allByParentCode) {
                CommonAreaGeoVo copy = BeanCopyUtils.copy(commonAreaSelectVo, CommonAreaGeoVo.class);
                if (null != commonAreaSelectVo.getPolyline()) {
                    copy.setPolyline(GeometryUtils.getWkt(commonAreaSelectVo.getPolyline()));
                }
                geoVos.add(copy);
            }
        }
        return geoVos;
    }

}
